Objective-C –反射篇
Objective-C –反射篇
Objective-C –反射篇 (2013-08-21 15:57:36)转载▼
标签: 杂谈 分类: iphone开发
\
今天我们来讲讲Objective-C的反射,
\
一般Java开发工程师听到Objective-C支持反射一定很兴奋吧!
\
\
\
基本反射
基本反射包括
\
获取Class 或 根据字符串获取Class
检查是否有selector 以及 根据字符串 获取selector 并执行
检查继承关系
基本反射就是能通过NSObject的一些方法和简单封装好的方法直接能进行反射的操作
\
Class相关的一些操作
首先就是获取一个实例的Class: [self class]
\
这个就是获取self对应实例的Class类型
\
也可以通过[类名 class]的方式获取Class,比如[UIView
class]和[[[UIView alloc] init] class]获取到的Class是一样的
\
- 当然最主要还得有类似Java的Class.forName(String)通过字符串直接获取到Class
- NSClassFromString
\
比如获取UIView的Class可以 NSClassFromString( “UIView”)
直接返回的就是UIView的Class
\
那么获取到Class有什么用呢?
\
直接通过Class来实例化对象
通过Class 你可以知道Class下面那些方法 属性 和 变量
,并可以直接访问他们(会在后面的搞基反射里面讲)
通过Class 直接实例化对象 很简单 比如
\
Class viewClass = NSClassFromString( “UIView”);
UIView *view = [viewClass alloc] init] ;
可以看到viewClass和UIView是等价的,包括对 + 类型方法的调用也是即 [UIView
layerClass]和[NSClassFromString( “UIView”) layerClass]是等价的
\
selector相关
selector对应的就是Java中的Method了 对应Method这个类 在Objective-C中是SEL
\
SEL是一个结构体的指针typedef struct objc_selector *SEL;
\
SEL 可以通过 selector和NSSelectorFromString来直接获取
\
而SEL和Method的不同在于 SEL在Mac系统中是单例的 .
\
即[Foo count]和[Bar count] 里面的count 指向的是同一个指针,
\
包括 selector(count)和NSSelectorFromString( “count”)指向的也都是同一个指针
\
这和Java每个Class用getMethod取出的Method都是单独的实例是不同的
\
SEL对应的就是方法的名字 ,
这和Objective-C的实现有关,就是方法对应的是消息,而SEL就是消息的名称,所以不同的实例可以使用相同的消息名,而消息名本身是单例的,不和实例本身产生关系
\
然后通过- (BOOL)respondsToSelector:(SEL)aSelector
可以判断实例是否真的有对于selector的实现,不管是否有被声明.
\
而要反射调用一个selector则可以通过一系列的performSelector:方法进行实现
比如
\
继承关系
类似Java 的 instanceOf Objective-C 也有类似的方法,有
\
- (BOOL)isKindOfClass:(Class)aClass
- (BOOL)isMemberOfClass:(Class)aClass
- (BOOL)isSubclassOfClass:(Class)aClass
- (BOOL)conformsToProtocol:(Protocol *)aProtocol
这几个方法都是定义在NSObject上的,区别在于
\
isKindOfClass 基本和Java 的 instanceOf的功能一致 ,
\
而isMemberOfClass 不能识别到父类 只能表明到底是不是这个Class ,
\
而isSubclassOfClass是+类型的方法和isKindOfClass一样的,不过就是通过Class来进行调用,
\
conformsToProtocol则是识别实例是否符合特定协议
\
高级反射
高级反射基本就是类似于Java的整个反射体系了,只不过Objective-C的这部分反射都是通过C调用实现的,比起来比较苦逼
\
主要的一些函数有:
\
objc_msgSend 系列
class/protocol 系列
method/SEL/IMP 系列
ivar /property系列
大部分的调用走包含在
\
#import
#import
这两个头文件里
\
objc_msgSend
看名字就能知道 这个是objective-c的消息发送函数
,上一篇也讲到所有的Objective-C的调用全是通过objc_msgSend来实现的
\
objc_msgSend的使用还是比较简单的,看id objc_msgSend(id theReceiver, SEL
theSelector, …)就能知道.
\
这里就介绍一些技巧
\
由于objc_msgSend 返回的是id 那么如果方法定义的是 基本类型怎么办?
\
看个样例
\
unsigned retForUnsigned = ((unsigned ( *)(id, SEL))
objc_msgSend)(self, NSSelectorFromString(nsPropertyName));
通过这种cast就可以返回cast为对于的基本类型
\
而如果返回是浮点的话 可以直接调用double objc_msgSend_fpret(id self,
SEL op, …)
\
那么还有一种情况就是返回的是一个struct的话 需要调用 void
objc_msgSend_stret(void * stretAddr, id theReceiver, SEL theSelector,
…) 来完成
\
当然 他们都有对应的super函数来直接调用父类的方法,如objc_msgSendSuper
\
实际上objc_XXX/object_XXX方法等方法都能找到对于的Objective-C的方法
\
不过有一个比较有意思的 可以向大家介绍一下
\
那就是void objc_setAssociatedObject(id object, const void *key, id
value, objc_AssociationPolicy policy) 和 id
objc_getAssociatedObject(id object, const void *key)
\
使用这一对函数就可以动态的为对象加getter/setter方法
\
大家知道使用Categroy是不能直接加property的,但是通过上面一对就可以
\
可以看AFNetworking中的代码
\
static char kAFImageRequestOperationObjectKey;
\
interface UIImageView (_AFNetworking)
property(readwrite, nonatomic, retain, setter =
af_setImageRequestOperation:) AFImageRequestOperation
*af_imageRequestOperation;
end
\
implementation UIImageView (_AFNetworking)
dynamic af_imageRequestOperation;
end
\
#pragma mark -
\
implementation UIImageView (AFNetworking)
\
- (AFHTTPRequestOperation *)af_imageRequestOperation {
return (AFHTTPRequestOperation *)
objc_getAssociatedObject(self,&kAFImageRequestOperationObjectKey);
}
\
- (void)af_setImageRequestOperation:(AFImageRequestOperation
*)imageRequestOperation {
objc_setAssociatedObject(self,
&kAFImageRequestOperationObjectKey,imageRequestOperation,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
不是设置 synthesize而是设置 dynamic +
objc_getAssociatedObject/objc_setAssociatedObject 来完成动态的属性添加
\
class/protocol
对应的class_XXX和protocol_XXX函数 这里面的方法基本NS都包含了
\
不过这里我们看一个声明
\
struct objc_class {
Class isa;
\
#if !__OBJC2__
Class super_class
OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version
OBJC2_UNAVAILABLE;
long info
OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists
OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols
OBJC2_UNAVAILABLE;
#endif
\
} OBJC2_UNAVAILABLE;
这是一个objectc class的原始定义 从里面就能看到一个Class
都包含了那些东西哦
\
method/SEL/IMP
这里说一下概念
\
Method就是方法 实际上他包含了SEL和IMP 不同于SEL它是有宿主的,并不是单例
\
SEL在上面已经介绍了实际上他就是等价于方法的名字
\
而IMP实际就是方法的真正实现了
\
如果要做动态方法解析 那么就可以自己作IMP来转换SEL对于的实现
\
ivar /property
ivar就是定义的变量,而property就是属性了
\
这里要注意的就是取出一个class的ivar/property 用到的类似函数
\
objc_property_t *class_copyPropertyList(Class cls, unsigned int
*outCount)
\
注意到它是copy的,也就是说这块内存是copy 你得自己负责最后去
\
例子:
\
unsigned int propertyCount;
objc_property_t *pProperty = class_copyPropertyList(class,
&propertyCount);
if (pProperty && propertyCount > 0) {
for (unsigned int i = 0; i <</span> propertyCount; i++) {
[self setPropertyToObject:o pProperty:pProperty[i]
withDepth:depth AndClass:class];
}
}
if (pProperty) {
free(pProperty);
}
不过这里有个比较苦逼的事情就是
去的ivar/property的类型值,这里Objective-C使用属性类型编码来区分类型
\
所以最后通过const char *property_getAttributes(objc_property_t
property)取到的是一个字符串, 得自己解析这个字符串来取得类型
\
对于的编码:
\
属性声明
属性描述
property char charDefault;
Tc,VcharDefault
property double doubleDefault;
Td,VdoubleDefault
property enum FooManChu enumDefault;
Ti,VenumDefault
property float floatDefault;
Tf,VfloatDefault
property int intDefault;
Ti,VintDefault
property long longDefault;
Tl,VlongDefault
property short shortDefault;
Ts,VshortDefault
property signed signedDefault;
Ti,VsignedDefault
property struct YorkshireTeaStruct structDefault;
T{YorkshireTeaStruct=”pot”i”lady”c},VstructDefault
property YorkshireTeaStructType typedefDefault;
T{YorkshireTeaStruct=”pot”i”lady”c},VtypedefDefault
property union MoneyUnion unionDefault;
T(MoneyUnion=”alone”f”down”d),VunionDefault
property unsigned unsignedDefault;
TI,VunsignedDefault
property int (*functionPointerDefault)(char *);
T\^?,VfunctionPointerDefault
property id idDefault; Note: the compiler warns: no ‘assign’, ‘retain’,
or ‘copy’ attribute is specified – ‘assign’ is assumed”
T ,VidDefault
property int *intPointer;
T\^i,VintPointer
property void *voidPointerDefault;
T\^v,VvoidPointerDefault
property int intSynthEquals;
In the implementation block:
synthesize intSynthEquals=_intSynthEquals;
Ti,V_intSynthEquals
property(getter=intGetFoo, setter=intSetFoo:) int intSetterGetter;
Ti,GintGetFoo,SintSetFoo:,VintSetterGetter
property(readonly) int intReadonly;
Ti,R,VintReadonly
property(getter=isIntReadOnlyGetter, readonly) int intReadonlyGetter;
Ti,R,GisIntReadOnlyGetter
property(readwrite) int intReadwrite;
Ti,VintReadwrite
property(assign) int intAssign;
Ti,VintAssign
property(retain) id idRetain;
T ,&,VidRetain
property(copy) id idCopy;
T ,C,VidCopy
property(nonatomic) int intNonatomic;
Ti,VintNonatomic
property(nonatomic, readonly, copy) id idReadonlyCopyNonatomic;
T ,R,C,VidReadonlyCopyNonatomic
property(nonatomic, readonly, retain) id idReadonlyRetainNonatomic;
T ,R,&,VidReadonlyRetainNonatomic
下面有个小程序用来解析这个属性编码
\
- (PropertyAttributeInfo *)analyseProperty:(objc_property_t)pProperty
WithClass:(Class)aClass {
\
NSString *propertyAttributes = [NSString
stringWithUTF8String:property_getAttributes(pProperty)];
NSMutableString *propertyName = [NSMutableString
stringWithUTF8String:property_getName(pProperty)];
PropertyAttributeInfo *info;
if ((info = [[PropertyAttributeInfoCache instance]
getFromCacheWithClass:aClass
AndPropertyName:propertyName]) != nil) {
return info;
}
TypeOfProperty typeOfProperty = NIL;
Class class = nil;
BOOL readOnly = NO;
Class arrayClass = nil;
NSString *dicPropertyName = propertyName;
NSArray *array = [propertyAttributes
componentsSeparatedByString: “,”];
NSString *typeAtt = [array objectAtIndex:0];
if ([typeAtt hasPrefix: “Tc”]) {
typeOfProperty = CHAR;
} else if ([typeAtt hasPrefix: “Td”]) {
typeOfProperty = DOUBLE;
} else if ([typeAtt hasPrefix: “Ti”]) {
typeOfProperty = INT;
} else if ([typeAtt hasPrefix: “Tf”]) {
typeOfProperty = FLOAT;
} else if ([typeAtt hasPrefix: “Tl”]) {
typeOfProperty = LONG;
} else if ([typeAtt hasPrefix: “Ts”]) {
typeOfProperty = SHORT;
} else if ([typeAtt hasPrefix: “T{“]) {
typeOfProperty = STRUCT;
} else if ([typeAtt hasPrefix: “TI”]) {
typeOfProperty = UNSIGNED;
} else if ([typeAtt hasPrefix: “T^i”]) {
typeOfProperty = INT_P;
} else if ([typeAtt hasPrefix: “T^v”]) {
typeOfProperty = VOID_P;
} else if ([typeAtt hasPrefix: “T^?”]) {
typeOfProperty = BLOCK;
} else if ([typeAtt hasPrefix: “T “]) {
typeOfProperty = ID;
if ([typeAtt length] > 4) {
class = NSClassFromString([typeAtt
substringWithRange:NSMakeRange(3, [typeAtt length] - 4)]);
if ([class isSubclassOfClass:[NSArray class]]) {
NSUInteger location = [propertyName
rangeOfString: “$“].location;
if (location != NSNotFound) {
arrayClass = NSClassFromString([propertyName
substringWithRange:NSMakeRange(location + 1,
[propertyName length] - location - 1)]);
dicPropertyName = [NSString
stringWithString:[propertyName substringWithRange:NSMakeRange(0,
location)]];
}
}
}
}
\
if ([array count] > 2) {
for (NSUInteger i = 1; i <</span> [array count] - 1; i++)
{
NSString *att = [array objectAtIndex:i];
if ([att isEqualToString: “R”]) {
readOnly = YES;
}
}
}
info = [[PropertyAttributeInfo alloc] init];
info.readOnly = readOnly;
info.class = class;
info.type = typeOfProperty;
info.arrayClass = arrayClass;
info.dicPropertyName = dicPropertyName;
info.oriPropertyName = propertyName;
\
[[PropertyAttributeInfoCache instance] putToCacheWithClass:aClass
AndPropertyName:propertyName
WithInfo:info];
return info;
\
}